﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmlEngine
{
    public static class EnumerationExtensions
    {
        #region Generic

        /// <summary>
        /// Returns whether or not an enumeration contains the provided type.
        /// </summary>
        /// <typeparam name="T">The type to check.</typeparam>
        /// <param name="type">The type to check.</param>
        /// <param name="value">The enumeration to check.</param>
        public static bool Has<T>(this Enum type, T value)
        {
            try
            {
                return (((int)(object)type & (int)(object)value) == (int)(object)value);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Returns whether or not a value is only the provided type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool Is<T>(this Enum type, T value)
        {
            try
            {
                return (int)(object)type == (int)(object)value;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Appends a new value into this enum.
        /// </summary>
        /// <typeparam name="T">A type(?)</typeparam>
        /// <param name="type">The enum to add to.</param>
        /// <param name="value">The value to add.</param>
        /// <returns>The new enum(?)</returns>
        public static T Add<T>(this Enum type, T value)
        {
            try
            {
                return (T)(object)(((int)(object)type | (int)(object)value));
            }
            catch (InvalidCastException ex)
            {
                throw new ArgumentException(
                    string.Format(
                        "Could not append value from enumerated type '{0}'.",
                        typeof(T).Name
                        ), ex);
            }
        }

        /// <summary>
        /// Removes the given value from the enum.
        /// </summary>
        /// <typeparam name="T">A type.</typeparam>
        /// <param name="type">The enum to remove from.</param>
        /// <param name="value">The value to remove</param>
        /// <returns>The resulting enum(?)</returns>
        public static T Remove<T>(this Enum type, T value)
        {
            try
            {
                return (T)(object)(((int)(object)type & ~(int)(object)value));
            }
            catch(InvalidCastException ex)
            {
                throw new ArgumentException(
                    string.Format(
                        "Could not remove value from enumerated type '{0}'.",
                        typeof(T).Name
                        ), ex);
            }
        }

        #endregion

        #region Specific

        #region SlopeDirection

        /// <summary>
        /// Checks if a SlopeDirection enum is sloping.
        /// </summary>
        /// <param name="slope">The SlopeDirection to check.</param>
        /// <returns>Whether the SlopeDirection is sloping or not.</returns>
        public static bool IsSloping(this SlopeDirection slope)
        {
            return slope != SlopeDirection.None;
        }

        /// <summary>
        /// Translates a SlopeDirection into a Direction enum.
        /// </summary>
        /// <param name="slope">The SlopeDirection to translate.</param>
        /// <returns>The translated Direction.</returns>
        public static Directions ToDirections(this SlopeDirection slope)
        {
            Directions directions;

            if (slope == SlopeDirection.BottomLeft)
                directions = Directions.Bottom | Directions.Left;
            else if (slope == SlopeDirection.BottomRight)
                directions = Directions.Bottom | Directions.Right;
            else if (slope == SlopeDirection.TopLeft)
                directions = Directions.Top | Directions.Left;
            else if (slope == SlopeDirection.TopRight)
                directions = Directions.Top | Directions.Right;
            else
                directions = Directions.None;

            return directions;
        }

        #endregion

        #region GridDirection
        /// <summary>
        /// Inverts a GridDirection (top becomes bottom, etc.)
        /// </summary>
        /// <param name="direction">The GridDirection to invert.</param>
        /// <returns>The inverted GridDirection.</returns>
        public static GridDirection Invert(this GridDirection direction)
        {
            switch (direction)
            {
                case GridDirection.Bottom:
                    return GridDirection.Top;
                case GridDirection.Top:
                    return GridDirection.Bottom;
                case GridDirection.Left:
                    return GridDirection.Right;
                case GridDirection.Right:
                    return GridDirection.Left;
                case GridDirection.BottomLeft:
                    return GridDirection.TopRight;
                case GridDirection.TopRight:
                    return GridDirection.BottomLeft;
                case GridDirection.BottomRight:
                    return GridDirection.TopLeft;
                case GridDirection.TopLeft:
                    return GridDirection.BottomRight;
                default: return GridDirection.Top;
            }
        }

        #endregion

        #endregion
    }
}
